/**
 * Bakalarska prace - Inteligentni budik
 * @package sleepCatcher
 * @file SCSession.cpp
 * @author Jan Pesava - xpesav00
 * @email xpesav00@stud.fit.vutbr.cz
 * @date 24. 12. 2011
 */

#include "models/SCSession.h"
#include "SCDatabase.h"
#include "SCSettings.h"
#include "models/SCHelpers.h"
#include "SCFormManager.h"

SCSession::SCSession()
{
	this->pWindowProbe = new SCWindow();
	this->pWindowProbe->SetWindowSize(SCSettings::GetWindowSize() * 60);
	this->pMaxExtrems = new ArrayList();
	this->pMinExtrems = new ArrayList();
	this->pCollectedSamples = new ArrayList();
	this->samplesCount = 0;

	//graph window
	this->pGraphWindow = new Queue();
	this->graphWindowMaxSize = 10;
	this->graphWindowEnergy = 0;
	this->graphWindowMaxEnergy = 0;

	this->SetDefaultValues();
}

SCSession::SCSession(int id)
{
	this->id = id;
	this->Load();

	this->pMaxExtrems = NULL;
	this->pMinExtrems = NULL;
	this->pCollectedSamples = NULL;
}

SCSession::~SCSession()
{
	if(this->pMaxExtrems != NULL)
	{
		this->pMaxExtrems->RemoveAll();
		delete this->pMaxExtrems;
	}

	if(this->pMinExtrems != NULL)
	{
		this->pMinExtrems->RemoveAll();
		delete this->pMinExtrems;
	}

	//collected samples
	if(this->pCollectedSamples != NULL)
	{
		this->pCollectedSamples->RemoveAll(true);
		delete this->pCollectedSamples;
	}

	delete this->pGraphWindow;
}

void
SCSession::SetType(int type)
{
	this->type = type;
}

int
SCSession::GetType()
{
	return this->type;
}


DateTime
SCSession::GetStartDate()
{
	return this->startDate;
}

void
SCSession::SetStartDate(DateTime dateTime)
{
	this->startDate = dateTime;
}

DateTime
SCSession::GetEndDate()
{
	return this->endDate;
}

void
SCSession::SetEndDate(DateTime dateTime)
{
	this->endDate = dateTime;
}

DateTime
SCSession::GetStopDate()
{
	return this->stopDate;
}

void
SCSession::SetStopDate(DateTime dateTime)
{
	this->stopDate = dateTime;
}

int
SCSession::GetQualityOfSleep()
{
	return this->qualityOfSleep;
}

void
SCSession::SetQualityOfSleep(int quality)
{
	this->qualityOfSleep = quality;
}

int
SCSession::GetSamplesCount()
{
	return this->samplesCount;
}

void
SCSession::SetSamplesCount(int count)
{
	this->samplesCount = count;
}


SCWindow*
SCSession::GetWindowProbe()
{
	return this->pWindowProbe;
}


void
SCSession::SetWindowProbe(SCWindow *pWindow)
{
	this->pWindowProbe = pWindow;
}


ArrayList*
SCSession::GetMaxExtreme()
{
	return this->pMaxExtrems;
}


void
SCSession::SetMaxExteme(ArrayList* pList)
{
	this->pMaxExtrems = pList;
}


ArrayList*
SCSession::GetMinExtreme()
{
	return this->pMinExtrems;
}


void
SCSession::SetMinExteme(ArrayList* pList)
{
	this->pMinExtrems = pList;
}

void
SCSession::SetSleepGraph(Bitmap* pGraph)
{
	this->pSleepGraph = pGraph;
}

Bitmap*
SCSession::GetSleepGraph()
{
 return this->pSleepGraph;
}


ArrayList*
SCSession::GetAllSamples()
{
	return SCDatabase::GetAllSamplesOfSession(this->GetId());
}


void
SCSession::AddSample(SCSample* pSample)
{
	Float *pSampleEnergy = new Float(pSample->GetEnergy());

	//Update counter
	this->samplesCount++;

	//Update ComputeWindow
	this->pWindowProbe->AddSample(pSample, pSampleEnergy);

	//Update GraphWindow
	Float* pSampleTmp;
	if(this->pGraphWindow->GetCount() < this->graphWindowMaxSize)
	{
		this->pGraphWindow->Enqueue(*pSampleEnergy);
		this->graphWindowEnergy += pSampleEnergy->ToFloat();
	} else {
		pSampleTmp = static_cast<Float*>(this->pGraphWindow->Dequeue());
		this->graphWindowEnergy += pSampleEnergy->ToFloat() - pSampleTmp->ToFloat();
		this->pGraphWindow->Enqueue(*pSampleEnergy);
	}

	//Set Max
	float windowAvarage = this->graphWindowEnergy / this->pGraphWindow->GetCount();
	if(this->graphWindowMaxEnergy < windowAvarage)
	{
		this->graphWindowMaxEnergy = windowAvarage;
	}

	//AppLog("Current window energy is %f and GraphWindowMaxEnergy is %f", windowAvarage, this->graphWindowMaxEnergy);

	//Add sample to collection
	this->pCollectedSamples->Add(*pSampleEnergy);
}


void
SCSession::DeleteSample(int id)
{
	SCDatabase::DeleteSample(id);
}


bool
SCSession::IsAwakeTime()
{
	if(this->GetAwakeTimeIn() > 0)
	{
		return false;
	} else {
		return true;
	}
}


void
SCSession::SetNextPart()
{
	SCSample* pSampleMax = this->pWindowProbe->GetPMaxEnergy();
	SCSample* pSampleMin = this->pWindowProbe->GetPMinEnergy();

	this->pMaxExtrems->Add(*pSampleMax);
	this->pMinExtrems->Add(*pSampleMin);
	this->pWindowProbe->SetNextPart();
}


void
SCSession::AddMaxExtreme(SCSample* pSample)
{
	this->pMaxExtrems->Add(*pSample);
}


void
SCSession::AddMinExtreme(SCSample* pSample)
{
	this->pMinExtrems->Add(*pSample);
}


SCSample*
SCSession::GetLastMaxExtreme()
{
	SCSample* pLastExtreme = NULL;

	if(this->pMaxExtrems->GetCount() > 0)
	{
		SCSample* pLastSavedSample = (SCSample*)this->pMaxExtrems->GetAt(this->pMaxExtrems->GetCount()-1);
		DateTime currentTime = SCHelpers::GetCurrentTime();
		int partSize = SCSettings::GetPartLengthTicks();

		if((pLastSavedSample->GetTime().GetTimeOfDay().GetTicks() + partSize) < currentTime.GetTimeOfDay().GetTicks())
		{
			return this->pWindowProbe->GetPMaxEnergy();
		} else if(this->pWindowProbe->GetPMaxEnergy()->GetWindowEnergy() > pLastSavedSample->GetWindowEnergy()) {
			return this->pWindowProbe->GetPMaxEnergy();
		} else {
			pLastExtreme = pLastSavedSample;
		}
	}

	return pLastExtreme;
}


SCSample*
SCSession::GetLastMinExtreme()
{
	// Not Implemented
	return NULL;
}


bool
SCSession::IsSampleAdd(ArrayList* pList, SCSample* pSample)
{
	// Not Implemented
	return true;
}

void
SCSession::Delete(int id)
{
	SCDatabase::DeleteSession(id);

	//delete img
	String path = "/Home/Sessions/Graphs/";
	path.Append(id);
	path.Append(".png");

	if(File::IsFileExist(path))
	{
		File::Remove(path);
	}
}


SCAlarm*
SCSession::GetActiveAlarm()
{
	return this->pActiveAlarm;
}

void
SCSession::SetActiveAlarm(SCAlarm* pActiveAlarm)
{
	this->pActiveAlarm = pActiveAlarm;
}

ArrayList*
SCSession::GetAllSessions()
{
	return SCDatabase::GetAllSessions();
}

SCSession*
SCSession::GetSession(int id)
{
	SCSession* pSession;

	if(id == 0)
	{
		pSession = new SCSession();
	} else {
		pSession = SCDatabase::GetSession(id);
	}

	return pSession;
}


SCSession*
SCSession::GetLastSession()
{
	SCSession* pLastSession = NULL;
	SCSession* pSessionTmp = NULL;
	ArrayList* pSessionsList = SCDatabase::GetAllSessions();

	if(pSessionsList != NULL)
	{
		for(int i=0; i < pSessionsList->GetCount(); i++)
		{
			pSessionTmp = static_cast<SCSession*>(pSessionsList->GetAt(i));

			if(pLastSession == NULL
					|| pLastSession->GetId() < pSessionTmp->GetId())
			{
				pLastSession = pSessionTmp;
			}
		}
	}
	return pLastSession;
}


bool
SCSession::Load()
{
	SCDatabase::LoadSession(this);
	return true;
}

bool
SCSession::Save()
{
	SCDatabase::SaveSession(this);
	return true;
}

void
SCSession::SetDefaultValues()
{
	this->SetStartDate(SCHelpers::GetCurrentTime());
	this->qualityOfSleep = 0;
}

void
SCSession::SetStopInformation()
{
	//save Session
	this->SetStopDate(SCHelpers::GetCurrentTime());
	SCDatabase::GetAllSessions()->Add(*this);
	this->Save();

	//compute sleep period
	int periodLength = this->ComputeSleepPeriod();
	if(periodLength != 0)
	{
		SCSettings::SetSleepPeriod((SCSettings::GetSleepPeriod() + periodLength) / 2);
		SCSettings::Save();
	}

	//create sleep graph
	this->CreateSleepGraph();

	//cache
	int index = SCDatabase::GetAllSessions()->GetCount() - 1;
	SCDatabase::AddDataModification(SCDatabase::ACTION_ADD, SCFormManager::IDF_SESSIONS, index);
	SCDatabase::AddDataModification(SCDatabase::ACTION_ADD, SCFormManager::IDF_SESSIONS_DELETE, index);
	SCDatabase::SetReloadSessions(true);
	SCDatabase::SetReloadSessionsDelete(true);
}

int
SCSession::ComputeSleepPeriod()
{
	int periodLength = 0;
	long long periodLengthSum = 0;
	SCSample* pBeforeSample;
	SCSample* pNextSample;

	if(this->pMaxExtrems->GetCount() > 1)
	{
		for(int i = 1; i < this->pMaxExtrems->GetCount(); i++)
		{
			pBeforeSample = static_cast<SCSample*>(this->pMaxExtrems->GetAt(i-1));
			pNextSample = static_cast<SCSample*>(this->pMaxExtrems->GetAt(i));
			periodLengthSum += pNextSample->GetTime().GetTimeOfDay().GetTicks() - pBeforeSample->GetTime().GetTimeOfDay().GetTicks();
		}
		periodLength = periodLengthSum / this->pMaxExtrems->GetCount() / TimeSpan::NUM_OF_TICKS_IN_MINUTE;
	}

	return periodLength;
}

int
SCSession::GetAwakeTimeIn()
{
	SCSample* pLastSample = this->GetLastMaxExtreme();

	int alarmTime = this->pActiveAlarm->GetTime().GetTimeOfDay().GetTicks();
	int x = SCSettings::GetAwakeTimeDifferenceTicks();
	int t = SCHelpers::GetCurrentTime().GetTimeOfDay().GetTicks();

	if(pLastSample != NULL)
	{
		int R = pLastSample->GetTime().GetTimeOfDay().GetTicks();
		int T = SCSettings::GetSleepPeriodTicks();

		int y = t - R;
		int ttr = T - y;

		if(ttr - x <= 0)
		{
			//AppLog("ttr = %i", ttr);
			return ttr;
		} else if(ttr - x > y) {
			//AppLog("0");
			return 0;
		} else {
			//AppLog("x1 = %i", x);
			return x;
		}
	} else {
		int timeToWakeUp = alarmTime - t;
		if(timeToWakeUp < 0)
			timeToWakeUp = 0;
		//AppLog("x2 = %i", timeToWakeUp);
		return timeToWakeUp;
	}
}

void
SCSession::CreateSleepGraph()
{
	if(this->pCollectedSamples->GetCount() > 0)
	{
		Canvas* pGraphCanvas;
		Bitmap* pGraphBitmap, *pGraphBitmapBG;
		Image* pImage;
		int width = 480;
		int height = 130;
		IList* pPointList = this->CreatePointListN(width, height);

		//set savePath
		String savePath("/Home/Sessions/Graphs/");
		savePath.Append(this->GetId());
		savePath.Append(".png");

		//setBackground
		pImage = new Image();
		pImage->Construct();
		pGraphBitmapBG = pImage->DecodeN(L"/Home/Icons/Commons/sleepGraphBG.png", BITMAP_PIXEL_FORMAT_RGB565);

		pGraphCanvas = new Canvas();
		pGraphCanvas->Construct(Rectangle(0, 0, width, height));
		pGraphCanvas->DrawBitmap(Point(0,0), *pGraphBitmapBG);
		pGraphCanvas->SetLineWidth(4);

		//draw polygon
		//pGraphCanvas->Set
		pGraphCanvas->FillPolygon(Color::COLOR_BLACK, *pPointList);
		pGraphCanvas->DrawPolygon(*pPointList);

		// save Image
		pGraphBitmap = new Bitmap();
		pGraphBitmap->Construct(*pGraphCanvas, pGraphCanvas->GetBounds());
		pImage->EncodeToFile(*pGraphBitmap, IMG_FORMAT_PNG, savePath, false);

		//dealoc
		delete pImage;
		delete pGraphBitmap;
		delete pGraphCanvas;
		delete pGraphBitmapBG;
		pPointList->RemoveAll(true);
		delete pPointList;
	}
}

SCSample*
SCSession::GetYetMaxPoint()
{
	SCSample* pSample = null;
	SCSample* pSampleTmp = null;

	if(this->pMaxExtrems->GetCount() > 0)
	{
		for(int i = 0; i < this->pMaxExtrems->GetCount(); i++)
		{
			pSampleTmp = static_cast<SCSample*>(this->pMaxExtrems->GetAt(i));
			if(pSample == NULL || (pSampleTmp->GetWindowEnergy() > pSample->GetWindowEnergy()))
			{
				pSample = pSampleTmp;
			}
		}
	} else {
		pSample = this->pWindowProbe->GetPMaxEnergy();
	}

	return pSample;
}

Osp::Base::Collection::IList*
SCSession::CreatePointListN(int width, int height)
{
	IList* pPointList = new ArrayList();
	int collectedSamplesCount = this->pCollectedSamples->GetCount();

	if(collectedSamplesCount > 0)
	{
		Float* pSample;
		int x, y, prevX = 0, prevY = height;
		float differenceX, differenceY;
		float maxY = 0;

		maxY = this->graphWindowMaxEnergy;

		//set difference
		differenceX = (float)width / collectedSamplesCount;
		differenceY = (float)height / maxY;

		//create points
		pPointList->Add(*(new Point(0, 0)));

		//define and set window
		this->pGraphWindow->RemoveAll();
		this->graphWindowEnergy = 0;
		Float* pSampleTmp;

		for(int i = 0; i < collectedSamplesCount; i++)
		{
			pSample = static_cast<Float*>(this->pCollectedSamples->GetAt(i));

			//Update Window
			if(pGraphWindow->GetCount() < graphWindowMaxSize)
			{
				this->pGraphWindow->Enqueue(*pSample);
				this->graphWindowEnergy += pSample->ToFloat();
			} else {
				pSampleTmp = static_cast<Float*>(this->pGraphWindow->Dequeue());
				this->graphWindowEnergy += pSample->ToFloat() - pSampleTmp->ToFloat();
				this->pGraphWindow->Enqueue(*pSample);
			}

			//Compute points
			x = i * differenceX;
			y = height - (this->graphWindowEnergy / this->pGraphWindow->GetCount()) * differenceY;

			//add point
			pPointList->Add(*(new Point(x,y)));

			prevX = x;
			prevY = y;
		}
		pPointList->Add(*(new Point(width, 0)));

		//dealloc
		pGraphWindow->RemoveAll();
	}

	return pPointList;
}

void SCSession::StartCatch()
{
	// Not Implemented
}

void SCSession::StopCatch()
{
	this->SetStopInformation();

	//dealloc
	this->pMaxExtrems->RemoveAll(true);
	this->pMinExtrems->RemoveAll(true);
	this->pCollectedSamples->RemoveAll(true);
	delete this->pWindowProbe;
}
